/**********************************************************
 * Copyright © 2024 Walkline Wang (https://walkline.wang)
 * Gitee: https://gitee.com/walkline/keypad-for-ops
 **********************************************************/
#include "ch9329.h"

HardwareSerial Serial_2(UART2_RXD, UART2_TXD);

void CH9329::setup() {
#if DEBUG_PROMPT
    Serial.println("CH9329 Setup");
#endif

    Serial_2.begin(UART2_BAUDRATE);
    while (!Serial_2) {
        ;
    }
}

void CH9329::reset() { send_hid_data(CMD_RESET); }

ch9329_err_t CH9329::get_chip_info() { return send_hid_data(CMD_GET_INFO); }

ch9329_err_t CH9329::receive_hid_data() {
    ch9329_err_t result = CH9329_SUCCESS;

    check_hid_data();

    if (hid_data.length() == 0) {
        return CH9329_UNKNOWN;
    }

#if DEBUG_DATA
    Serial.print("=== hid recv data: <== [");
    for (uint8_t count = 0; count < hid_data.length(); count++) {
        Serial.print("0x");
        Serial.print(hid_data[count], HEX);
        Serial.print(", ");
    }
    Serial.println("]");
#endif

    if (hid_data[0] != head[0] || hid_data[1] != head[1] || hid_data[2] != 0) {
        return CH9329_UNKNOWN;
    }

    switch (hid_data[3] - RETURN_MASK) {
    case CMD_GET_INFO:
        parse_chip_info_data();
        result = CH9329_GET_INFO;
        break;

#if DEBUG_PROMPT && DEBUG_DATA
    case CMD_SEND_KB_GENERAL_DATA:
        result = hid_data[5];

        if (result == CH9329_SUCCESS) {
            Serial.println("Keyboard data sent");
        } else {
            Serial.print("Keyboard data send error, code: ");
            Serial.println(result);
        }

        break;
    case CMD_SEND_KB_MEDIA_DATA:
        break;
    case CMD_SEND_MS_ABS_DATA:
        break;
    case CMD_SEND_MS_REL_DATA:
        result = hid_data[5];

        if (result == CH9329_SUCCESS) {
            Serial.println("Mouse data sent");
        } else {
            Serial.print("Mouse data send error, code: ");
            Serial.println(result);
        }

        break;
    case CMD_SEND_MY_HID_DATA:
        break;
    case CMD_READ_MY_HID_DATA:
        break;
    case CMD_GET_PARA_CFG:
        break;
    case CMD_SET_PARA_CFG:
        break;
    case CMD_GET_USB_STRING:
        break;
    case CMD_SET_USB_STRING:
        break;
    case CMD_SET_DEFAULT_CFG:
        break;
    case CMD_RESET:
        Serial.println("CH9329 reset"); /* never triggered */
        break;
    default:
        Serial.println("Unknown command");
#endif
    }

    return result;
}

ch9329_err_t CH9329::send_hid_data(uint8_t command) {
    return send_hid_data(command, NULL, 0);
}

ch9329_err_t CH9329::send_hid_data(uint8_t command, int8_t *data,
                                   uint8_t length) {
    uint8_t command_length = PACKAGE_LENGTH + length;
    uint8_t package[command_length] = {};

    package[0] = head[0];
    package[1] = head[1];
    package[2] = head[2];
    package[3] = command;
    package[4] = length;

    if (length > 0) {
        for (uint8_t count = 0; count < length; count++) {
            package[5 + count] = data[count];
        }
    }

    package[command_length - 1] =
        calculate_checksum(package, command_length - 1);

    Serial_2.write(package, command_length);

#if DEBUG_DATA
    Serial.print("=== hid sent data: ==> [");
    for (uint8_t count = 0; count < command_length; count++) {
        Serial.print("0x");
        Serial.print(package[count], HEX);
        Serial.print(", ");
    }
    Serial.println("]");
#endif

    if (command == CMD_RESET) {
        delay(1000);
        return receive_hid_data();
    } else if (command == CMD_GET_INFO) {
        delay(50);
        return receive_hid_data();
    } else {
        delay(20);
    }

#if DEBUG_DATA
    return receive_hid_data();
#else
    return CH9329_SUCCESS;
#endif
}

void CH9329::check_hid_data() {
    hid_data = "";

    while (Serial_2.available()) {
        hid_data += char(Serial_2.read());
        delayMicroseconds(10);
    }
}

void CH9329::parse_chip_info_data() {
    String version = "v1.";
    version += String(hid_data[5] % 0x10);

    strcpy(chip_info.version, version.c_str());
    chip_info.usb_status = hid_data[6];
    chip_info.num_lock = bitRead(hid_data[7], 0);
    chip_info.caps_lock = bitRead(hid_data[7], 1);
    chip_info.scroll_lock = bitRead(hid_data[7], 2);
}

uint8_t CH9329::calculate_checksum(const uint8_t *data, uint8_t length) {
    uint16_t sum_value = 0;

    for (uint8_t index = 0; index < length; index++) {
        sum_value += data[index];
    }

    return (uint8_t)(sum_value & 0xff);
}
